package ui.flowManager;

import java.util.HashMap;
import java.util.LinkedList;
import java.io.File;

import main.Main;

import org.w3c.dom.*;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.DocumentBuilder;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException; 


import org.apache.log4j.Logger;

import ui.flowManager.expirimentProperties.ExperimentProperties;
import ui.flowManager.expirimentProperties.ExperimentProperties.Agent_Group;
import ui.panels.Panel;
import ui.panels.PanelFactory;
import ui.panels.ThirdPanel;
import ui.panels.primary.adaptiveAgentProperties.AdaptiveAgentProperties;
import ui.panels.primary.playerProperties.PlayerPropertiesPanel_2;
import ui.panels.primary.players.PlayersPanel;
import ui.panels.primary.second.SecondPanel;

/**
 * @author vladimir && alon
 *
 * Singleton pattern 
 */
public class FlowManagerImpl implements FlowManager {

	private static FlowManagerImpl instance = null;
	private final static String initFileName=System.getProperty(Main.EXPORT_DIR)+"resource\\init.xml";
	//private final static String initFileName="init.xml";
	private LinkedList<HashMap<String, String>> flow = null;
	private ExperimentProperties 				props = null;
	private int 								panelIndex = 0;

	Logger log = Logger.getLogger(this.getClass());
	
	private JFrame 		guiFrame 	= null;
	private Panel		lastPanel	= null;
	
	/**
	 * Return the instance of the Singleton
	 * @return instance
	 */
	public static FlowManagerImpl getInstance()
	{
		if (instance == null)
		{
			instance = new FlowManagerImpl();
		}
		
		return instance;
	}
	
	private FlowManagerImpl(){
		props = new ExperimentProperties();
		flow = initFlow();
		log.debug("FlowManagerImpl initiated");
		UpdatePanelsData();		
	}

	public void createNextPanel() {
		//finds out if to continue or finish
		if(panelIndex<flow.size()){//has more
			
			try{
				int id=Integer.parseInt(flow.get(panelIndex).get("id"));
				//creates a panel according to the id of next in line
				log.debug("creating panel with id: "+id);
				lastPanel =	PanelFactory.create(id,flow.get(panelIndex));
				panelIndex++;
				guiFrame.getContentPane().removeAll();
				lastPanel.show(props);
				guiFrame.getContentPane().add(lastPanel);		
				guiFrame.pack();
			}catch (NumberFormatException e) {
				log.error(e);
			}catch (Exception e) {
				log.error(e);
			}
		}else{
			finish();
		}
		
	}

	
	public void createPriviousPanel()
	{
		//panelIndex = 1 means that the first panel is shown
		if(panelIndex < 2)
		{
			return;
		}
		
		try{
			//go back twice 
			panelIndex -= 2;
			int id=Integer.parseInt(flow.get(panelIndex).get("id"));
			//creates a panel according to the id of next in line
			log.debug("creating panel with id: "+id);
			lastPanel =	PanelFactory.create(id,flow.get(panelIndex));
			panelIndex++;
			guiFrame.getContentPane().removeAll();
			lastPanel.show(props);
			guiFrame.getContentPane().add(lastPanel);		
			guiFrame.pack();
		}catch (NumberFormatException e) {
			log.error(e);
		}catch (Exception e) {
			log.error(e);
		}
	}
	
	public void resetExperimentProperties()
	{
		props = new ExperimentProperties();
		log.debug("Experiment Properties was reset");
	}
	
	/**
	 * Updating The view of the players of specific game
	 * 
	 * @param gameNumber
	 */
	public void updatePlayersPanel (int gameNumber)
	{
		//case the it is the  ThirdPanel - panel that have the games,
		//  players && players properties panels
		if (lastPanel instanceof ThirdPanel)
		{
			ThirdPanel thirdPanel = (ThirdPanel)lastPanel;
			
			//the new players panel
			PlayersPanel playersPanel = new PlayersPanel(gameNumber);
			thirdPanel.addToPanelPlayersPanel(playersPanel, props);
			playersPanel.setGameLabel(gameNumber);
		}
	}
	
	/**
	 * 
	 */
	public void updatePlayerPropertiesPanel(int gameNumber, int playerNumber,Agent_Group group)
	{
		//case the it is the  ThirdPanel
		if (!(lastPanel  instanceof ThirdPanel)){
			log.info("not 3rd panel");
			return;
		}
		
		
		ThirdPanel thirdPanel = (ThirdPanel)lastPanel;
		if (group == Agent_Group.CONTROL_AGENT)
		{
			//the new players panel
			PlayerPropertiesPanel_2 playerPropertiesPanel = new PlayerPropertiesPanel_2(gameNumber, playerNumber);
			thirdPanel.addToPanelPlayerPropertiesPanel(playerPropertiesPanel, props);
			playerPropertiesPanel.setPlayerLabel(props);
		}
		//case studied agents
		else
		{
			AdaptiveAgentProperties adaptiveAgentProperties = new AdaptiveAgentProperties(gameNumber, playerNumber);
			thirdPanel.addToPanelPlayerPropertiesPanel(adaptiveAgentProperties, props);
			adaptiveAgentProperties.setPlayerLabel(props);
		}
	}
	
	
	/*public void emptyProperties()
	{
		if(!(lastPanel instanceof ThirdPanel))
			return;
		
		//cast
		ThirdPanel thirdPanel = (ThirdPanel)lastPanel;
		thirdPanel.emptyProperties();
	}*/
	
	
	public void finish() {
		//run experiment and return results
		log.debug("finish");
	}

	public void start() {
		panelIndex=0;
		guiFrame = new JFrame(); 
        //make sure the program exits when the frame closes
        guiFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        guiFrame.setTitle("Adaptive Agent Experiment Wizard");
        guiFrame.setSize(739,480);
        guiFrame.setResizable(false);
        //This will center the JFrame in the middle of the screen
        guiFrame.setLocationRelativeTo(null);
		createNextPanel();
		log.debug("show frame");
		guiFrame.setVisible(true);
	}
	
	public LinkedList<HashMap<String, String>> initFlow() {
		LinkedList<HashMap<String, String>> flow=new LinkedList<HashMap<String, String>>();
		//read from file
		try{
			DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance();
			DocumentBuilder docBuilder = docBuilderFactory.newDocumentBuilder();
			log.info("init file: "+initFileName);
			Document doc = docBuilder.parse (new File(initFileName));
			// normalize text representation
			doc.getDocumentElement ().normalize();
			log.debug("Root element of the doc is " + doc.getDocumentElement().getNodeName());
			
			NodeList listOfPanels = doc.getElementsByTagName("panel");
			int totalPanels = listOfPanels.getLength();
			log.debug("Total number of panels : " + totalPanels);

			for(int i=0; i<totalPanels; i++){
				Node panel = listOfPanels.item(i);
				log.debug("created map number "+i);
				HashMap<String, String> obj=new HashMap<String, String>();
				if (panel.getNodeType() == Node.ELEMENT_NODE) {
					Element eElement = (Element) panel;
					NodeList n=eElement.getChildNodes();
					for(int j=0;j<n.getLength();j++){
						String name=n.item(j).getNodeName();
						if(!name.equals("#text")){
							String value= getTagValue(name,eElement);
							obj.put(name,value);
							log.debug("added: "+name+" , "+value);
						}
					}
				}
				log.debug("added map number "+i);
				flow.add(obj);
			}
		}catch (SAXParseException err) {
			log.error ("** Parsing error" + ", line " 
					+ err.getLineNumber () + ", uri " + err.getSystemId() +" " + err.getMessage ());
		}catch (SAXException e) {
			Exception x = e.getException();
			log.error(((x == null) ? e : x).getStackTrace());
		}catch (Throwable t) {
			log.error(t.getMessage());
		}
		
		return flow;
	}
	
	
	/**
	 * Setting the panels Data structures for later setting the components of GUI
	 * Enough to execute just one setting for every panel because the data is transfered to
	 * 	static data structures in the panels
	 */
	public void UpdatePanelsData() {
		final int secondPanelIndex = 1;
		final int thirdPanelIndex = 2;

		
		//setting the players panel - the middle panel in the third Panel 
		HashMap<String, String> map = flow.get(secondPanelIndex);
		SecondPanel secondPanel = new SecondPanel();
		secondPanel.setFromPropertiesConfig(map);
		
		map = flow.get(thirdPanelIndex);
		ThirdPanel thirdPanel = new ThirdPanel();
		thirdPanel.setFromPropertiesConfig(map);
			
	}
	
	/**
	 *  Enable/Disable the JFrame - the all GUI
	 *  @param arg
	 */
	public void setEnable(boolean arg)
	{
		guiFrame.setEnabled(arg);
	}
	
	/*** @return lastPanel*/
	public Panel getLastPanel() {
		return lastPanel;
	}

	/*** @return props*/
	public ExperimentProperties getProps() {
		return props;
	}

	public LinkedList<HashMap<String, String>> getFlow() {
		return flow;
	}


	/**
	 * @param sTag tag name
	 * @param eElement
	 * @return return the tag value.
	 */
	private String getTagValue(String sTag, Element eElement) {
		NodeList nlList = eElement.getElementsByTagName(sTag).item(0).getChildNodes();
	 
	        Node nValue = (Node) nlList.item(0);
	 
		return nValue.getNodeValue();
	 }
	
}
